// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
 * Generated on: 2022/07/26
 * Function discription: Header file for implementing the board-level
 *                       configuration mechanism of the PCIe module
 */
#include "include/mach/pcie_chip.h"
#include "include/mach/pcie_core.h"

struct pcie_board_ini_attr g_pcie_board_attr = {0};

#define BOARD_ATTR(index, value) (g_pcie_board_attr.chan_attr[index].value)
#define BOARD_CFG(index, value) (g_pcie_board_cfg[index].value)
#define BOARD_QCA(index, value) (g_pcie_board_attr.qca_wifi_attr[index].value)

#define BOARD_GET(m_suffix, a_value, c_prefix, c_suffix, type) do { \
	(BOARD_##m_suffix(PCIE_INDEX_0, a_value) = \
		(type)BOARD_CFG(c_prefix##0_##c_suffix, value)); \
	(BOARD_##m_suffix(PCIE_INDEX_1, a_value) = \
		(type)BOARD_CFG(c_prefix##1_##c_suffix, value)); \
	(BOARD_##m_suffix(PCIE_INDEX_2, a_value) = \
		(type)BOARD_CFG(c_prefix##2_##c_suffix, value)); \
} while (0)

#define RESET_GPIO_GET(prefix, type) \
	BOARD_GET(ATTR, type##_reset_gpio, prefix, RESET_GPIO, uint8_t)

#define CHIP_TYPE_GET() \
	BOARD_GET(ATTR, wifi_chip_type, WIFI, CHIP_TYPE, uint32_t)

#define PCIE_ACP_CLOSE_GET() \
	BOARD_GET(ATTR, pcie_acp_close, PCIE, ACP_SWITCH, uint8_t)

#define PCIE_VOL_SWING() \
	BOARD_GET(ATTR, pcie_voltage_swing, PCIE, VOLTAGE_SWING, uint32_t)

#define PCIE_DRV_ABILITY() \
	BOARD_GET(ATTR, pcie_drv_ability, PCIE, DRV_ABILITY, uint8_t)

#define PCIE_MSI_ENHANCE() \
	BOARD_GET(ATTR, pcie_msi_enhance, PCIE, MSI_ENHANCE, uint8_t)

#define QCA_WIFI_GPIO(index_type, type) \
	BOARD_GET(QCA, type##_gpio, QCA_WIFI, index_type, uint8_t)

#define BOARD_ATTR_CHECK(index, value) do { \
	if (BOARD_ATTR(index, value) == PCIE_INVALID_INT32) \
		BOARD_ATTR(index, value) = BOARD_ATTR(PCIE_INDEX_0, value); \
} while (0)

#define CFG_INIT(attr) .key_word = attr, .value = PCIE_INVALID_INT32

/* PCIE support ini, cfg list */
static struct src_ini_section_parse g_pcie_board_cfg[PCIE_INI_CONFIG_MAX] = {
	/* Common attr */
	{ CFG_INIT("pcie_channel_cross") },  /* 00: channel cross */
	{ CFG_INIT("pcie_channel_select") }, /* 01: channel select */
	/* channel attr */
	{ CFG_INIT("pcie0_reset_gpio") },    /* 02: pcie0 reset gpio */
	{ CFG_INIT("pcie1_reset_gpio") },    /* 03: pcie1 reset gpio */
	{ CFG_INIT("pcie2_reset_gpio") },    /* 04: pcie2 reset gpio */
	{ CFG_INIT("wifi0_reset_gpio") },    /* 05: wifi0 reset gpio */
	{ CFG_INIT("wifi1_reset_gpio") },    /* 06: wifi1 reset gpio */
	{ CFG_INIT("wifi2_reset_gpio") },    /* 07: wifi2 reset gpio */
	/* if wifi1/wifi2 chip type is invalid, it would be assigned as type of wifi0 */
	{ CFG_INIT("wifi_chip_type") },      /* 08: wifi0 chip type */
	{ CFG_INIT("wifi1_chip_type") },     /* 09: wifi1 chip type */
	{ CFG_INIT("wifi2_chip_type") },     /* 10: wifi1 chip type */
	{ CFG_INIT("pcie0_irq_mode") },      /* 11: pcie0 interrupt type  */
	{ CFG_INIT("pcie1_irq_mode") },      /* 12: pcie1 interrupt type  */
	{ CFG_INIT("pcie2_irq_mode") },      /* 13: pcie2 interrupt type  */
	{ CFG_INIT("pcie0_voltage_swing") }, /* 14: pcie0 differential signal swing(voltage) */
	{ CFG_INIT("pcie1_voltage_swing") }, /* 15: pcie1 differential signal swing(voltage) */
	{ CFG_INIT("pcie2_voltage_swing") }, /* 16: pcie2 differential signal swing(voltage) */
	{ CFG_INIT("pcie0_drv_ability") }, /* 17: pcie0 differential signal drv ability(current) */
	{ CFG_INIT("pcie1_drv_ability") }, /* 18: pcie1 differential signal drv ability(current) */
	{ CFG_INIT("pcie2_drv_ability") }, /* 19: pcie2 differential signal drv ability(current) */
	{ CFG_INIT("pcie_acp_close") },      /* 20: pcie0 ACP enable cfg */
	{ CFG_INIT("pcie1_acp_close") },     /* 21: pcie1 ACP enable cfg */
	{ CFG_INIT("pcie2_acp_close") },     /* 22: pcie2 ACP enable cfg */
	{ CFG_INIT("pcie0_msi_enhance") },   /* 23: pcie0 msi interrupt affinity enhance */
	{ CFG_INIT("pcie1_msi_enhance") },   /* 24: pcie1 msi interrupt affinity enhance */
	{ CFG_INIT("pcie2_msi_enhance") },   /* 25: pcie2 msi interrupt affinity enhance */
	{ CFG_INIT("pcie_lane_init_mode") }, /* 26: pcie lane init mode select */
	/* QCN attr */
	{ CFG_INIT("wifi_qca_enable_gpio") },  /* 27: QCN wifi 5G FEM power control  */
	{ CFG_INIT("wifi1_qca_enable_gpio") }, /* 28: QCN wifi 5G FEM power control  */
	{ CFG_INIT("wifi2_qca_enable_gpio") }, /* 29: QCN wifi 5G FEM power control  */
	{ CFG_INIT("qca_wifi0_reset_gpio") },  /* 30: QCN wifi0 reset gpio */
	{ CFG_INIT("qca_wifi1_reset_gpio") },  /* 31: QCN wifi1 reset gpio */
	{ CFG_INIT("qca_wifi2_reset_gpio") },  /* 32: QCN wifi2 reset gpio */
	/* DY attr */
	{ CFG_INIT("ext_rf0_power_gpio") },  /* 33: DY wifi0 rf power control gpio */
	{ CFG_INIT("ext_rf1_power_gpio") },  /* 34: DY wifi1 rf power control gpio */
	/* DFX attr */
	{ CFG_INIT("dfx_pcie_voltage_amp") }, /* 35: DFX pcie voltage cfg */
	{ CFG_INIT("ssd_pcie_channel") },     /* 36: ssd attr */
	{ CFG_INIT("pcie_speed") },           /* 37: pcie speed */
	{ CFG_INIT("pcie_time") },            /* 38: pcie Link Setup Retry Times */
	{ CFG_INIT("wifi_3v3_pwr") },         /* 39: 3v3 power contrl gpio */
};

#if KER_F_DESC("ini cfg file")

int32_t pice_board_get_ini_config(void)
{
	get_section_cfg_funcptr *get_section_cfg_func;
	uint32_t ret;
	uint8_t section[] = "pcie";

	get_section_cfg_func = get_section_cfg_func_hook();
	if ((get_section_cfg_func == NULL) || (*get_section_cfg_func == NULL))
		return -EIO;

	ret = (*get_section_cfg_func)(section, g_pcie_board_cfg, ARRAY_SIZE(g_pcie_board_cfg));
	if (ret != HW_RET_SUCCESS) {
	/*
	 * failed to parse file, means no wifi, set all pcie select to 0,
	 * continue to load and return success
	 */
		pr_info("[PCIE] Read ini fail 0x%x, no need start pcie!\n", ret);
	}

	return HW_RET_SUCCESS;
}

void pcie_board_print_ini_config(void)
{
	uint32_t i;

	pr_info("Board pcie ini configure:\n");
	for (i = 0; i < ARRAY_SIZE(g_pcie_board_cfg); i++) {
		if ((g_pcie_board_cfg[i].value != PCIE_INVALID_INT32) &&
			(g_pcie_board_cfg[i].value != PCIE_INVALID_INT8)) {
			pr_info("%s: %u\n",
				g_pcie_board_cfg[i].key_word, g_pcie_board_cfg[i].value);
		}
	}
}

void pcie_board_get_comon_attr(void)
{
	g_pcie_board_attr.pcie_channel_select = g_pcie_board_cfg[PCIE_CHANNEL_SELECT].value;
	g_pcie_board_attr.pcie_channel_cross  = g_pcie_board_cfg[PCIE_CHANNEL_CROSS].value;
}

void pcie_board_get_chan_attr(void)
{
	RESET_GPIO_GET(PCIE, pcie);
	RESET_GPIO_GET(WIFI, wifi);

	/* pcie connected wifi type */
	CHIP_TYPE_GET();

	/* compatible with old product, if wifi1/2 type is invalid, use type of wifi0 */
	BOARD_ATTR_CHECK(PCIE_INDEX_1, wifi_chip_type);
	BOARD_ATTR_CHECK(PCIE_INDEX_2, wifi_chip_type);

	/* PCIE lanes ACP cfg */
	PCIE_ACP_CLOSE_GET();

	/* compatible with old product, if wifi1 acp cfg is invalid, use acp cfg of wifi0 */
	BOARD_ATTR_CHECK(PCIE_INDEX_1, pcie_acp_close);
	BOARD_ATTR_CHECK(PCIE_INDEX_2, pcie_acp_close);

	/* get interrupt mode, default INTx */
	g_pcie_board_attr.chan_attr[PCIE_INDEX_0].interrupt_mode =
		g_pcie_board_cfg[PCIE0_IRQ_MODE].value;
	if (g_pcie_board_attr.chan_attr[PCIE_INDEX_0].interrupt_mode == PCIE_INVALID_INT32)
		g_pcie_board_attr.chan_attr[PCIE_INDEX_0].interrupt_mode =
			PCIE_INTERRUPT_MODE_INTX;

	g_pcie_board_attr.chan_attr[PCIE_INDEX_1].interrupt_mode =
		g_pcie_board_cfg[PCIE1_IRQ_MODE].value;
	BOARD_ATTR_CHECK(PCIE_INDEX_1, interrupt_mode);
	g_pcie_board_attr.chan_attr[PCIE_INDEX_2].interrupt_mode =
		g_pcie_board_cfg[PCIE2_IRQ_MODE].value;
	BOARD_ATTR_CHECK(PCIE_INDEX_2, interrupt_mode);

	/* swing and drv ability */
	PCIE_VOL_SWING();
	PCIE_DRV_ABILITY();
	PCIE_MSI_ENHANCE();

	g_pcie_board_attr.ssd_pcie_channel = g_pcie_board_cfg[SSD_PCIE_CHANNEL].value;
	g_pcie_board_attr.pcie_speed = g_pcie_board_cfg[PCIE_SPEED].value;
	g_pcie_board_attr.pcie_time = g_pcie_board_cfg[PCIE_TIME].value;
	g_pcie_board_attr.wifi_3v3_pwr = g_pcie_board_cfg[WIFI_CHIP_3V3_PWR].value;
}

void pcie_board_get_lane_mode_attr(void)
{
	g_pcie_board_attr.pcie_lane_init_mode = g_pcie_board_cfg[PCIE_LANE_INIT_MODE].value;
}

void pcie_board_get_qca_attr(void)
{
	QCA_WIFI_GPIO(ENABLE_GPIO, wifi_qca_enable);
	QCA_WIFI_GPIO(RESET_GPIO, qca_wifi_reset);
}

void pcie_board_get_dy_attr(void)
{
	g_pcie_board_attr.dy_wifi_attr[PCIE_INDEX_0].ext_rf_power_gpio =
		(uint8_t)g_pcie_board_cfg[EXT_RF0_POWER_GPIO].value;
	if (g_pcie_board_attr.pcie_channel_select & PCIE1_MODE_SEL) {
		g_pcie_board_attr.dy_wifi_attr[PCIE_INDEX_1].ext_rf_power_gpio =
			(uint8_t)g_pcie_board_cfg[EXT_RF1_POWER_GPIO].value;
	} else if (g_pcie_board_attr.pcie_channel_select & PCIE2_MODE_SEL) {
		g_pcie_board_attr.dy_wifi_attr[PCIE_INDEX_2].ext_rf_power_gpio =
			(uint8_t)g_pcie_board_cfg[EXT_RF1_POWER_GPIO].value;
	}
}

void pcie_get_dfx_amplitude(void)
{
	/* get cfg from ini file */
	uint32_t ret;
	uint32_t value;
	union soc_dfx_ctrl dfx_ctrl = {0};

	g_pcie_board_attr.dfx_pcie_voltage_amp = g_pcie_board_cfg[DFX_PCIE_VOLTAGE_AMP].value;
	ret = read_reg_iomap_value(PCIE_SWING_DFX_REG, &value);
	if (ret != HW_RET_SUCCESS)
		return;

	ret = read_reg_iomap_value(PCIE_SOC_DFX_CTRL_REG, &(dfx_ctrl.value));
	if (ret != HW_RET_SUCCESS)
		return;

	/* if dfx cfg is not 0 and flag bit is not 0, update dfx to board attr */
	if ((value != 0) && (dfx_ctrl.bits.pcie_swing_set != 0))
		g_pcie_board_attr.dfx_pcie_voltage_amp = value;
}

void pcie_board_get_wifi_chip_board_attr(void)
{
	uint32_t pcie_index;
	struct wifi_hi1152_cfg hi1152_cfg = {0};
	struct wifi_mxl_cfg mxl_cfg = {0};
	struct wifi_dy1153_cfg dy1153_cfg = {0};

	for (pcie_index = 0; pcie_index < PCIE_INDEX_MAX; pcie_index++) {
		uint32_t wifi_chip_type = g_pcie_board_attr.chan_attr[pcie_index].wifi_chip_type;

		switch (wifi_chip_type) {
		case WIFI_CHIP_TYPE_1152:
		case WIFI_CHIP_TYPE_1152_CRYSTAL:
			pcie_get_1152_board_attr(&hi1152_cfg);
			break;
		case WIFI_CHIP_TYPE_MXL:
			pcie_get_mxl_board_attr(&mxl_cfg);
			break;
		case WIFI_CHIP_TYPE_1153:
			pcie_get_dy1153_board_attr(&dy1153_cfg);
			break;
		default:
			break;
		}
	}
}

int32_t pcie_board_parse_ini_cfg(void)
{
	/* get cfg from ini file */
	int32_t ret;

	ret = pice_board_get_ini_config();
	if (ret != HW_RET_SUCCESS)
		return ret;

	/* print all valid cfg for board now */
	pcie_board_print_ini_config();
	pcie_board_get_comon_attr();
	pcie_board_get_chan_attr();
	pcie_board_get_lane_mode_attr();
	pcie_board_get_qca_attr();
	pcie_board_get_dy_attr();
	pcie_get_dfx_amplitude();

	pcie_board_get_wifi_chip_board_attr();
	return HW_RET_SUCCESS;
}
#endif

#if KER_F_DESC("convert data by Wi-Fi type")

void pcie_clear_common_attr(uint32_t wifi_chip_type)
{
	uint32_t pcie_index;

	for (pcie_index = 0; pcie_index < PCIE_INDEX_MAX; pcie_index++) {
		if (wifi_chip_type == g_pcie_board_attr.chan_attr[pcie_index].wifi_chip_type) {
			g_pcie_board_attr.chan_attr[pcie_index].pcie_reset_gpio = PCIE_INVALID_INT8;
			g_pcie_board_attr.chan_attr[pcie_index].wifi_reset_gpio = PCIE_INVALID_INT8;
			g_pcie_board_attr.dy_wifi_attr[pcie_index].ext_rf_power_gpio =
				PCIE_INVALID_INT8;
		}
	}
}

void pcie_get_1152_board_attr(struct wifi_hi1152_cfg *wifi_cfg)
{
	/* complete convert for the first obtaining */
	static struct wifi_hi1152_cfg wifi_1152_attr;
	static bool is_first = true;

	if (wifi_cfg == NULL)
		return;

	if (!is_first) {
		*wifi_cfg = wifi_1152_attr;
		return;
	}

	wifi_1152_attr.pcie_rst_gpio = g_pcie_board_attr.chan_attr[PCIE_INDEX_0].pcie_reset_gpio;
	wifi_1152_attr.wl_en_gpio = g_pcie_board_attr.chan_attr[PCIE_INDEX_0].wifi_reset_gpio;
	wifi_1152_attr.pmu_pwren_gpio = g_pcie_board_attr.chan_attr[PCIE_INDEX_1].pcie_reset_gpio;
	wifi_1152_attr.ctl_0v9_gpio = g_pcie_board_attr.chan_attr[PCIE_INDEX_1].wifi_reset_gpio;

	pcie_clear_common_attr(WIFI_CHIP_TYPE_1152);
	pcie_clear_common_attr(WIFI_CHIP_TYPE_1152_CRYSTAL);
	is_first = false;
	*wifi_cfg = wifi_1152_attr;
}

void pcie_get_mxl_board_attr(struct wifi_mxl_cfg *wifi_cfg)
{
	static struct wifi_mxl_cfg wifi_mxl_attr;
	static bool is_first = true;

	if (wifi_cfg == NULL)
		return;

	/* complete convert for the first obtaining */
	if (!is_first) {
		*wifi_cfg = wifi_mxl_attr;
		return;
	}

	wifi_mxl_attr.wifi_reset_gpio = g_pcie_board_attr.chan_attr[PCIE_INDEX_1].wifi_reset_gpio;
	wifi_mxl_attr.ctl_0v9_gpio = g_pcie_board_attr.chan_attr[PCIE_INDEX_1].pcie_reset_gpio;

	pcie_clear_common_attr(WIFI_CHIP_TYPE_MXL);
	is_first = false;
	*wifi_cfg = wifi_mxl_attr;
}

static void pcie_get_info(struct wifi_dy1153_cfg *wifi_cfg, int index)
{
	wifi_cfg->pcie1_reset_gpio =
		g_pcie_board_attr.chan_attr[index].pcie_reset_gpio;
	wifi_cfg->ext_rf1_power_gpio =
		g_pcie_board_attr.dy_wifi_attr[index].ext_rf_power_gpio;
	wifi_cfg->wifi1_power_gpio =
		g_pcie_board_attr.chan_attr[index].wifi_reset_gpio;
}

void pcie_get_dy1153_board_attr(struct wifi_dy1153_cfg *wifi_cfg)
{
	static struct wifi_dy1153_cfg wifi_1153_attr;
	static bool is_first = true;

	if (wifi_cfg == NULL)
		return;

	/* complete convert for the first obtaining */
	if (!is_first) {
		*wifi_cfg = wifi_1153_attr;
		return;
	}

	wifi_1153_attr.wifi_3v3_power_gpio = (uint8_t)g_pcie_board_attr.wifi_3v3_pwr;
	wifi_1153_attr.pcie0_reset_gpio = g_pcie_board_attr.chan_attr[PCIE_INDEX_0].pcie_reset_gpio;
	wifi_1153_attr.ext_rf0_power_gpio =
		g_pcie_board_attr.dy_wifi_attr[PCIE_INDEX_0].ext_rf_power_gpio;
	wifi_1153_attr.wifi0_power_gpio = g_pcie_board_attr.chan_attr[PCIE_INDEX_0].wifi_reset_gpio;
	/* set para based on actual using pcie channel */
	if (g_pcie_board_attr.pcie_channel_select & PCIE1_MODE_SEL)
		pcie_get_info(&wifi_1153_attr, PCIE_INDEX_1);
	else if (g_pcie_board_attr.pcie_channel_select & PCIE2_MODE_SEL)
		pcie_get_info(&wifi_1153_attr, PCIE_INDEX_2);

	pcie_clear_common_attr(WIFI_CHIP_TYPE_1153);
	is_first = false;
	*wifi_cfg = wifi_1153_attr;
}

void pcie_get_board_attr(struct pcie_board_ini_attr **pcie_board_attr)
{
	if (pcie_board_attr == NULL)
		return;

	*pcie_board_attr = &g_pcie_board_attr;
}
#endif

#if KER_F_DESC("convert data by Wi-Fi type")

static bool pcie_wifi_chip_type(uint32_t pcie_index, int type[], int type_len)
{
	int index;

	if (pcie_index >= PCIE_INDEX_MAX)
		return false;

	for (index = 0; index < type_len; index++) {
		if (g_pcie_board_attr.chan_attr[pcie_index].wifi_chip_type == type[index])
			return true;
	}

	return false;
}

bool pcie_is_wifi_mxl_chip(uint32_t pcie_index)
{
	int type[] = { WIFI_CHIP_TYPE_MXL };

	return pcie_wifi_chip_type(pcie_index, type, ARRAY_SIZE(type));
}

bool pcie_is_wifi_cel_chip(uint32_t pcie_index)
{
	int type[] = { WIFI_CHIP_TYPE_CELENO };

	return pcie_wifi_chip_type(pcie_index, type, ARRAY_SIZE(type));
}

bool pcie_is_wifi1152_chip(uint32_t pcie_index)
{
	int type[] = { WIFI_CHIP_TYPE_1152, WIFI_CHIP_TYPE_1152_CRYSTAL };

	return pcie_wifi_chip_type(pcie_index, type, ARRAY_SIZE(type));
}

bool pcie_is_wifi1153_chip(uint32_t pcie_index)
{
	int type[] = { WIFI_CHIP_TYPE_1153 };

	return pcie_wifi_chip_type(pcie_index, type, ARRAY_SIZE(type));
}

bool pcie_is_wifi115x_chip(uint32_t pcie_index)
{
	int type[] = { WIFI_CHIP_TYPE_1152, WIFI_CHIP_TYPE_1152_CRYSTAL, WIFI_CHIP_TYPE_1153 };

	return pcie_wifi_chip_type(pcie_index, type, ARRAY_SIZE(type));
}

bool pcie_is_wifi_qca_chip(uint32_t pcie_index)
{
	int type[] = { WIFI_CHIP_TYPE_QCA };

	return pcie_wifi_chip_type(pcie_index, type, ARRAY_SIZE(type));
}

bool pcie_is_extlsw_chip(uint32_t pcie_index)
{
	int type[] = { EXTLSW_CHIP_TYPE_RTL931X, EXTLSW_CHIP_TYPE_LSW2125 };

	return pcie_wifi_chip_type(pcie_index, type, ARRAY_SIZE(type));
}

bool pcie_is_extlsw_type2125(uint32_t pcie_index)
{
	int type[] = { EXTLSW_CHIP_TYPE_LSW2125 };

	return pcie_wifi_chip_type(pcie_index, type, ARRAY_SIZE(type));
}

uint32_t pcie_get_interrupt_mode(uint32_t pcie_index)
{
	if (pcie_index >= PCIE_INDEX_MAX)
		return PCIE_INTERRUPT_MODE_BUTT;

	return g_pcie_board_attr.chan_attr[pcie_index].interrupt_mode;
}

inline bool pcie_is_support_msi_enhance(uint32_t pcie_index)
{
	if ((pcie_index < PCIE_INDEX_MAX) &&
	    (g_pcie_board_attr.chan_attr[pcie_index].pcie_msi_enhance == PCIE_MSI_ENHANCE_EN))
		return true;

	return false;
}

#endif
